<template>
  <el-form
    :model="modelValue"
    ref="nodeRef"
    :label-width="labelWidth"
    :inline="inline"
    :rules="rules"
  >
    <el-form-item
      v-for="item of items"
      :key="item.labelId"
      :label="item.label"
      :prop="item.key"
      :required="item.required"
      :error="item.error"
      :rules="handleRule(item)"
    >
      <component
        :is="`cmp-${item.type}`"
        :item="item"
        v-model:form="modelValue"
        :options="options"
        :autocompleteOptions="autocompleteOptions"
        :disabled="disabled"
        :operate-type="operateType"
      />
    </el-form-item>
  </el-form>
</template>

<script lang="ts">
import { defineComponent, onMounted, PropType, reactive } from 'vue'
import { FormItemKey, ObjectType } from 'typings/interfaces'
import { map, hasIn } from 'lodash'
import useValidate from '@/hooks/useValidate'
import { useI18n } from 'vue-i18n'
import { handleFormItems } from '@/utils/util'
import { FormItem, FormItems, Options, Types } from './types'
import { OperateTypes } from '@/enums/operateTypes'
import CmpInput from './Item/Input.vue'
import CmpSelect from './Item/Select.vue'
import CmpInputNumber from './Item/InputNumber.vue'
import CmpDatePick from './Item/DatePick.vue'
import CmpUser from './Item/User/index.vue'
import CmpUpload from './Item/UploadV2.vue'
import CmpSelectOrg from './Item/SelectOrg.vue'
import CmpRadio from './Item/Radio.vue'
import CmpCascader from './Item/Cascader.vue'
import CmpAutocomplete from './Item/Autocomplete.vue'

export default defineComponent({
  components: {
    CmpInput,
    CmpSelect,
    CmpUser,
    CmpInputNumber,
    CmpDatePick,
    CmpUpload,
    CmpSelectOrg,
    CmpRadio,
    CmpCascader,
    CmpAutocomplete,
  },
  emits: ['update:modelValue'],
  props: {
    modelValue: {
      type: Object as PropType<ObjectType>,
      required: true,
    },
    options: {
      type: Object as PropType<Options>,
      default: () => ({}),
    },
    autocompleteOptions: {
      type: Object as PropType<ObjectType>,
      default: () => ({}),
    },
    formItems: {
      type: Array as PropType<FormItems>,
      required: true,
    },
    initValues: {
      type: Object as PropType<ObjectType>,
      default: () => ({}),
    },
    labelWidth: {
      type: String as PropType<string>,
      required: true,
    },
    inline: {
      type: Boolean,
      default: false,
    },
    initialValues: {
      type: Object as PropType<ObjectType>,
      default: () => ({}),
    },
    rules: {
      type: Object as PropType<
        Record<
          FormItemKey,
          Array<{
            required: boolean
            message: string
            validator?: (...args: any) => any
            trigger?: 'blur' | 'change'
          }>
        >
      >,
      default: () => ({}),
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    operateType: {
      type: String as PropType<OperateTypes | null>,
      required: false,
    },
  },
  setup(props) {
    const { t } = useI18n()
    const { nodeRef } = useValidate()
    const form = reactive({
      ...handleFormItems(props.formItems),
      ...props.initialValues,
    })

    const handleRule = (item: FormItem) => {
      if (!props.rules[item.key] && item.required) {
        return [
          {
            required: true,
            message: t('lang.validate.required'),
            validator: (rule, value, cb) => {
              if (item.type === Types.inputNumber && hasIn(item, 'min') && value >= item['min']) {
                cb()
              }
              if (value) {
                cb()
              } else {
                cb(new Error(t('lang.validate.required')))
              }
            },
          },
        ]
      }
    }

    /* 初始化清除表单校验 */
    onMounted(() => {
      nodeRef.value?.resetFields()
    })

    const items = map(props.formItems, (item) => ({ ...item, label: t(item.labelId) }))
    return {
      nodeRef,
      form,
      items,
      handleRule,
    }
  },
})
</script>
